package frpc.manager.lock;

import fdupc.countext.SpringContextUtil;
import frpc.manager.operation.*;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 类描述:  Redis对象业务处理逻辑实现
 *
 * @author 王雷
 * @version 1.0.0
 * @date 2020/11/26 0026 下午 4:30
 */
@Slf4j
@SuppressWarnings("unchecked")
public class RedisLockManagerImpl implements IRedisLockManager {

    /**
     * 属性描述：Redis业务处理接口
     *
     * @date : 2021/4/16 0016 下午 3:53
     */
    protected final IRedisManage iRedisManage;

    /**
     * 功能描述：Redis对象业务处理逻辑实现
     *
     * @param iRedisManage Redis业务处理接口
     * @author : 王雷
     * @date : 2020/11/26 0026 下午 4:47
     */
    protected RedisLockManagerImpl(IRedisManage iRedisManage) {
        this.iRedisManage = iRedisManage;
    }

    private static final String LOCK_KEY_NAME = "redission-lock-";

    /**
     * 功能描述：获取Redis操作处理对象
     *
     * @date : 2022/10/3 0003 上午 11:17
     */
    @Override
    public IRedisManage giveRedisManage() {
        return iRedisManage;
    }

    /**
     * 功能描述：自动加锁并执行业务逻辑
     *
     * @param lockKey                  锁定名称
     * @param waitTime                 执行超时时间
     * @param redisLockSuccessFunction 正确处理函数
     * @param trRedisLockErrorFunction 异常处理函数
     * @param managerFunction          回调处理函数
     * @return R 返回泛型
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T, R> R autoLockToManager(String lockKey, int waitTime, RedisLockSuccessFunction<T, R> redisLockSuccessFunction
            , RedisLockErrorFunction<R> trRedisLockErrorFunction, RedisLockManagerFunction<T> managerFunction) {
        if (lockKey == null || "".equals(lockKey.trim())) {
            return trRedisLockErrorFunction.errorWorkManager(iRedisManage, new Exception("没有指定需要锁定的Key"));
        }

        final Redisson redisson = SpringContextUtil.giveBeanByClass(Redisson.class);
        final RLock redisLock = redisson.getLock(LOCK_KEY_NAME + lockKey);

        waitTime = (waitTime <= 0) ? 10 : waitTime;
        try {
            final boolean acquireLock = redisLock.tryLock(waitTime, 100, TimeUnit.SECONDS);
            //如果获取到了时间，这里开始执行逻辑，如果没有获取到，超时，直接退出；
            if (acquireLock) {
                try {
                    return redisLockSuccessFunction.successWorkManager(iRedisManage, managerFunction.workManager(iRedisManage));
                } catch (Exception e) {
                    log.warn("锁逻辑处理失败", e);
                    return trRedisLockErrorFunction.errorWorkManager(iRedisManage, e);
                } finally {
                    redisLock.unlock();
                }
            } else {
                throw new Exception("获取锁超时；没有获取到锁");
            }
        } catch (Exception e) {
            log.warn("获取锁逻辑处理异常", e);
            return trRedisLockErrorFunction.errorWorkManager(iRedisManage, e);
        }

    }

    /**
     * 功能描述：自动加锁并执行业务逻辑
     *
     * @param lockKey                  锁定名称
     * @param redisLockSuccessFunction 正确处理函数
     * @param trRedisLockErrorFunction 异常处理函数
     * @param managerFunction          回调处理函数
     * @return R 返回泛型
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T, R> R autoLockToManager(String lockKey, RedisLockSuccessFunction<T, R> redisLockSuccessFunction
            , RedisLockErrorFunction<R> trRedisLockErrorFunction, RedisLockManagerFunction<T> managerFunction) {
        return autoLockToManager(lockKey, 10, redisLockSuccessFunction, trRedisLockErrorFunction, managerFunction);
    }

    /**
     * 功能描述：自动加锁并执行业务逻辑
     *
     * @param lockKey         需要锁定的键
     * @param waitTime        等待获取锁的时间
     * @param managerFunction 锁定需要处理的函数
     * @exception Exception 执行异常
     * @author : 王雷
     * @date : 2019/10/17 0017 下午 2:29
     */
    @Override
    public <T> T autoLockToManager(String lockKey, int waitTime, RedisLockManagerFunction<T> managerFunction) throws Exception {

        if (lockKey == null || "".equals(lockKey.trim())) {
            throw new Exception("没有指定需要锁定的Key");
        }

        //TODO 通过Redisson获取Redis锁
        final Redisson redisson = SpringContextUtil.giveBeanByClass(Redisson.class);
        final RLock redisLock = redisson.getLock(LOCK_KEY_NAME + lockKey);

        waitTime = (waitTime <= 0) ? 10 : waitTime;
        final boolean acquireLock = redisLock.tryLock(waitTime, 100, TimeUnit.SECONDS);
        //如果获取到了时间，这里开始执行逻辑，如果没有获取到，超时，直接退出；
        if (acquireLock) {
            try {
                return managerFunction.workManager(iRedisManage);
            } catch (Exception e) {
                log.warn("锁逻辑处理失败", e);
                throw e;
            } finally {
                redisLock.unlock();
            }
        } else {
            throw new Exception("获取锁超时；没有获取到锁");
        }
    }

    /**
     * 功能描述：自动加锁并执行业务逻辑
     *
     * @param lockKey         需要锁定的键
     * @param managerFunction 锁定需要处理的函数
     * @author : 王雷
     * @throws Exception 执行异常
     * @date : 2019/10/17 0017 下午 2:29
     */
    @Override
    public <T> T autoLockToManager(String lockKey, RedisLockManagerFunction<T> managerFunction) throws Exception {
        return autoLockToManager(lockKey, 10, managerFunction);
    }

    /**
     * 功能描述：获取Redis处理对象
     *
     * @date : 2022/6/17 0017 上午 11:55
     */
    protected <K, T> RedisTemplate<K, T> giveRedisTemplate() {
        return SpringContextUtil.giveBean("redisTemplate", RedisTemplate.class);
    }

    /**
     * 功能描述：限流处理逻辑
     *
     * @param limitingKey     限流标识Key
     * @param maxThreshold    限流最大阀值
     * @param limitingMessage 限流触发回调
     * @param managerFunction 限流执行的处理函数
     * @return T 返回泛型
     * @throws Exception 执行异常
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T> T limitingManager(String limitingKey, long maxThreshold, RedisLimitingMessageFunction<T> limitingMessage
            , RedisLockManagerFunction<T> managerFunction) throws Exception {
        final T t = limitingVerification(limitingKey, maxThreshold, limitingMessage);
        if (t != null) {
            return t;
        }
        try {
            return managerFunction.workManager(iRedisManage);
        } catch (Exception e) {
            log.warn("限流业务处理异常", e);
            throw e;
        } finally {
            giveRedisTemplate().opsForValue().increment(limitingKey, -1);
        }
    }

    /**
     * 功能描述：限流处理逻辑
     *
     * @param limitingKey              限流标识Key
     * @param maxThreshold             限流最大阀值
     * @param redisLockSuccessFunction 正确处理函数
     * @param trRedisLockErrorFunction 异常处理函数
     * @param limitingMessage          限流触发回调
     * @param managerFunction          限流执行的处理函数
     * @return T 返回泛型
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T, R> R limitingManager(String limitingKey, long maxThreshold
            , RedisLockSuccessFunction<T, R> redisLockSuccessFunction, RedisLockErrorFunction<R> trRedisLockErrorFunction
            , RedisLimitingMessageFunction<R> limitingMessage, RedisLockManagerFunction<T> managerFunction) {
        final R r = limitingVerification(limitingKey, maxThreshold, limitingMessage);
        if (r != null) {
            return r;
        }
        try {
            return redisLockSuccessFunction.successWorkManager(iRedisManage, managerFunction.workManager(iRedisManage));
        } catch (Exception e) {
            log.warn("限流业务处理异常", e);
            return trRedisLockErrorFunction.errorWorkManager(iRedisManage, e);
        } finally {
            giveRedisTemplate().opsForValue().increment(limitingKey, -1);
        }
    }

    /**
     * 功能描述：限流验证
     *
     * @param limitingKey     限流标识Key
     * @param maxThreshold    限流最大阀值
     * @param limitingMessage 限流触发回调
     * @return 处理结果
     * @date : 2022/6/17 0017 下午 12:58
     */
    protected <R> R limitingVerification(String limitingKey, long maxThreshold
            , RedisLimitingMessageFunction<R> limitingMessage) {
        final Long increment = giveRedisTemplate().opsForValue().increment(limitingKey, 1);
        if (!Objects.nonNull(increment)) {
            log.error("没有可用的：{}；数据", limitingKey);
            return limitingMessage.limitingMessage();
        }
        if (increment >= maxThreshold) {
            giveRedisTemplate().opsForValue().increment(limitingKey, -1);
            log.error("超过Limiting最大限制{}", maxThreshold);
            return limitingMessage.limitingMessage();
        }
        return null;
    }

    /**
     * 功能描述：限流兼加锁处理逻辑
     *
     * @param limitingKey              限流标识Key
     * @param maxThreshold             限流最大阀值
     * @param redisLockSuccessFunction 正确处理函数
     * @param trRedisLockErrorFunction 异常处理函数
     * @param limitingMessage          限流触发回调
     * @param lockKey                  锁定标识名称
     * @param expireTime               执行超时时间
     * @param managerFunction          锁执行的函数
     * @return T 返回泛型
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T, R> R limitingAndLockManager(String limitingKey, long maxThreshold
            , RedisLockSuccessFunction<T, R> redisLockSuccessFunction, RedisLockErrorFunction<R> trRedisLockErrorFunction
            , RedisLimitingMessageFunction<R> limitingMessage, String lockKey, int expireTime
            , RedisLockManagerFunction<T> managerFunction) {
        return limitingManager(limitingKey, maxThreshold, redisLockSuccessFunction, trRedisLockErrorFunction, limitingMessage
                , iRedisManage -> autoLockToManager(lockKey, expireTime, managerFunction));
    }

    /**
     * 功能描述：限流兼加锁处理逻辑
     *
     * @param limitingKey              限流标识Key
     * @param maxThreshold             限流最大阀值
     * @param redisLockSuccessFunction 正确处理函数
     * @param trRedisLockErrorFunction 异常处理函数
     * @param limitingMessage          限流触发回调
     * @param lockKey                  锁定标识名称
     * @param managerFunction          锁执行的函数
     * @return T 返回泛型
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T, R> R limitingAndLockManager(String limitingKey, long maxThreshold
            , RedisLockSuccessFunction<T, R> redisLockSuccessFunction, RedisLockErrorFunction<R> trRedisLockErrorFunction
            , RedisLimitingMessageFunction<R> limitingMessage, String lockKey
            , RedisLockManagerFunction<T> managerFunction) {
        return limitingManager(limitingKey, maxThreshold, redisLockSuccessFunction, trRedisLockErrorFunction, limitingMessage
                , iRedisManage -> autoLockToManager(lockKey, managerFunction));
    }

    /**
     * 功能描述：限流兼加锁处理逻辑
     *
     * @param limitingKey     限流标识Key
     * @param maxThreshold    限流最大阀值
     * @param limitingMessage 限流触发回调
     * @param lockKey         锁定标识名称
     * @param expireTime      执行超时时间
     * @param managerFunction 锁执行的函数
     * @return T 返回泛型
     * @throws Exception 执行异常
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T> T limitingAndLockManager(String limitingKey, long maxThreshold, RedisLimitingMessageFunction<T> limitingMessage
            , String lockKey, int expireTime, RedisLockManagerFunction<T> managerFunction) throws Exception {
        return limitingManager(limitingKey, maxThreshold, limitingMessage, iRedisManage -> autoLockToManager(lockKey, expireTime
                , managerFunction));
    }

    /**
     * 功能描述：限流兼加锁处理逻辑
     *
     * @param limitingKey     限流标识Key
     * @param maxThreshold    限流最大阀值
     * @param limitingMessage 限流触发回调
     * @param lockKey         锁定标识名称
     * @param managerFunction 锁执行的函数
     * @return T 返回泛型
     * @throws Exception 执行异常
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T> T limitingAndLockManager(String limitingKey, long maxThreshold, RedisLimitingMessageFunction<T> limitingMessage
            , String lockKey, RedisLockManagerFunction<T> managerFunction) throws Exception {
        return limitingManager(limitingKey, maxThreshold, limitingMessage, iRedisManage -> autoLockToManager(lockKey, managerFunction));
    }
}
